<template>
    <div>
        <!-- 面包屑导航区域 -->
        <el-breadcrumb separator-class="el-icon-arrow-right">
            <el-breadcrumb-item>集群管理</el-breadcrumb-item>
            <el-breadcrumb-item>集群全局管控</el-breadcrumb-item>
        </el-breadcrumb>

        <!-- 集群管理列表区 -->
        <div :style="innerStyle">
            <el-scrollbar style="height: 100%">

                <!-- 图标区 -->
                <div style="margin-bottom: 5px; display: flex;">
                    <div id="main_left" style="flex: 2; height:200px;">
                    </div>
                    <div id="main_right" style="flex: 1.5; height:200px;">
                    </div>
                </div>

                <!-- 提示信息 -->
                <el-alert style="margin-bottom: 5px; border: 1px dotted orange;"
                    title="注意：不包含节点的集群将不会出现在下面的 [列表] 中" 
                    type="warning" 
                    show-icon center>
                </el-alert>

                <!-- 表格区 -->
                <el-table 
                    style="margin: 0px;" 
                    :data="clusterList" 
                    :row-key="getRowKeys"
                    :expand-row-keys="expands"
                    border stripe>
                    
                    <!-- 表格行展开列 -->
                    <el-table-column type="expand">
                        <template slot-scope="scope">
                            <el-row :class="['v-center']">
                                <el-col style="width: 200px;">
                                    <div class="hdgap" style="display: inline-block;">

                                        <!-- text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black -->
                                        <!-- text-shadow: 1px 1px 5px black; -->
                                        <h2 style="padding: 1; margin: 0; text-align: center; color: white; text-shadow: 1px 1px 5px black; font-size: 18px;">{{scope.row.name}}</h2>
                                        <el-button type="primary" icon="el-icon-search" size="mini" round @click="haBrowse(scope.row.id)">浏览库服务状态</el-button><br>
                                        <el-button type="success" icon="el-icon-video-play" size="mini" round @click="haResume(scope.row.id)">开启高可用功能</el-button><br>
                                        <el-button type="danger" icon="el-icon-video-pause" size="mini" round @click="haSuspend(scope.row.id)">暂停高可用功能</el-button><br>
                                    </div>
                                </el-col>
                                <el-col>
                                    <el-row :class="['v-center', 'bdbottom', index == 0 ? 'bdtop' : '']" v-for="(item, index) in scope.row.nodes" :key="item.id">
                                        <el-col style="width: 300px;">
                                            <div :class="[ 'nodebox', item.master ? 'nodebox-master' : 'nodebox-standby']">
                                                <div class="nodebox-prefix">
                                                    {{ item.name }}
                                                </div>
                                                <div class="nodebox-content">
                                                    {{ item.ipaddr }}
                                                </div>
                                            </div>
                                        </el-col>
                                        <el-col style="margin-left: 5px;">
                                            <el-button v-if="!item.master" type="primary" icon="el-icon-sort" size="mini" round @click="switchMaster(scope.row.id, item.id)">切换为主节点</el-button>
                                        </el-col>
                                    </el-row>
                                </el-col>
                            </el-row>
                        </template>
                    </el-table-column>

                    <!-- 常规表格列 -->
                    <el-table-column label="#" type="index"></el-table-column>
                    <el-table-column label="集群名称" prop="name"></el-table-column>
                    <el-table-column label="兼容模式" prop="db_mode"></el-table-column>
                    <el-table-column label="HA高可用状态">
                        <i class="el-icon-loading"></i> <b style="font-weight: 400; color: gray;">获取中</b>
                    </el-table-column>
                    <el-table-column label="运行状态">
                        <i class="el-icon-loading"></i> <b style="font-weight: 400; color: gray;">获取中</b>
                    </el-table-column>
                    <el-table-column label="已运行时长">
                        <i class="el-icon-loading"></i> <b style="font-weight: 400; color: gray;">获取中</b>
                    </el-table-column>
                    <el-table-column label="操作" width="345px">
                        <template slot-scope="scope">
                            <el-button type="primary" icon="el-icon-search" size="mini" round @click="clusterBrowse(scope.row.id)">运行状态</el-button>
                            <el-button type="success" icon="el-icon-caret-right" size="mini" round @click="clusterStart(scope.row.id)">启动集群</el-button>
                            <el-button type="danger" icon="el-icon-turn-off" size="mini" round @click="clusterStop(scope.row.id)">停止集群</el-button>
                        </template>
                    </el-table-column>

                </el-table>
            </el-scrollbar>
        </div>

        <!-- 节点操作交互流程对话框 -->
        <el-dialog 
            :title="dialogTitle" 
            :visible.sync="processDialogVisible" 
            :close-on-click-modal="false"
            :close-on-press-escape="false"
            :show-close="false" 
            width="80%">

            <el-card class="card-timemessage">
                <div slot="header">
                    <span>数据库集群回执信息</span>
                    <div :style="'float: right; color: ' + processInfo.titleColor + ';'">
                        <i :class="processInfo.titleIcon"></i>
                        {{processInfo.titleName}}
                    </div>
                </div>
                <div class="div-timemessage" :style="'height: 450px;'">
                    <textarea
                        v-model="processInfo.log" 
                        style="background: black; color: white; word-wrap:normal; outline: none; border: none; resize: none; width: 100%; height: 100%; font-size: 12px; font-family: 'Courier New', Courier, monospace;" 
                        readonly>
                    </textarea>
                </div>
            </el-card>

            <span slot="footer" class="dialog-footer">
                <el-button type="danger" @click="closeProcessDialog" :disabled="inJobProcessing">关闭</el-button>
            </span>
        </el-dialog>
    </div>
</template>

<script>
import echarts from 'echarts'
import { mapGetters } from 'vuex'
export default {
    data: function () {
        return {
            chartLeft: {},
            optionLeft: {},
            chartRight: {},
            optionRight: {},
            expands: [],
            clusterList: [],
            clusterListAll: [],
            dialogTitle: '',
            jobData: {
                jobid: '0',
                status: 0,
                steps: [
                    {
                        name: '正在准备操作步骤...',
                        message: '即将开始执行命令，请稍候...',
                        status: 0
                    }
                ]
            },
            processDialogVisible: false,
            inJobProcessing: false,
            refreshDataFlag: false
        }
    },
    computed: {
        ...mapGetters(['getInnerSize']),
        innerStyle: function () {
            return `height: ${this.getInnerSize.height - 160}px; overflow: auto;`;
        },
        processInfo: function () {
            let titleIcon = 'el-icon-loading';
            let titleColor = '#E4E7ED';
            let titleName = '操作即将开始';
            let titleStyle = 'display: none;';
            let logContent = '';

            // 1.0 步骤状态
            const stepStatusCode = this.jobData.steps[0].status;
            if (stepStatusCode === 1) titleName = this.jobData.steps[0].name;
            if (stepStatusCode !== 0) {
                logContent += ` ${this.jobData.steps[0].name} \n`;
                logContent += `${this.jobData.steps[0].message}\n\n`;
            }

            // 2.0 任务状态
            const statusCode = this.jobData.status;
            if (statusCode === -1) {
                titleIcon = "el-icon-close";
                titleColor = '#FF0000';
                titleName = '操作执行失败';
                titleStyle = 'display: inline;';
            } else if (statusCode === 0) {
                titleIcon = "el-icon-warning-outline";
                titleColor = '#E4E7ED';
            } else if (statusCode === 1) {
                titleIcon = "el-icon-loading";
                titleColor = '#40AAFF';
            } else if (statusCode === 2) {
                titleIcon = "el-icon-check";
                titleName = '操作执行完成';
                titleColor = '#67C23A';
            } else {
                titleIcon = "el-icon-minus";
            }
            return {
                log: logContent,
                titleIcon: titleIcon,
                titleName: titleName,
                titleColor: titleColor,
                titleStyle: titleStyle
            };
        }
    },
    mounted: async function () {
        this.chartLeft = echarts.init(document.getElementById('main_left'));
        this.chartRight = echarts.init(document.getElementById('main_right'));
    },
    created: async function () {
        await this.getClusterList();

        // 1.0 填充图表数据 - 柱状图
        var optionLeft = {
            legend: {},
            tooltip: {},
            dataset: {
                source: [
                    ['product'],
                    ['数据库集群节点数']
                ]
            },
            xAxis: { type: 'category' },
            yAxis: {},
            series: []
        };

        // 2.0 填充图表数据 - 饼状图
        var optionRight = {
            tooltip: {
                trigger: 'item',
                formatter: '{a} <br/>{b}: {c} ({d}%)'
            },
            legend: {
                orient: 'vertical',
                left: 10,
                data: []
            },
            series: [
                {
                    name: '集群组成占比',
                    type: 'pie',
                    radius: ['50%', '90%'],
                    avoidLabelOverlap: false,
                    label: {
                        show: false,
                        position: 'center'
                    },
                    emphasis: {
                        label: {
                            show: true,
                            fontSize: '20',
                            fontWeight: 'bold'
                        }
                    },
                    labelLine: {
                        show: false
                    },
                    data: []
                }
            ]
        };
        this.clusterListAll.forEach((item, index) => {
            optionLeft.series.push({
                type: 'bar'
            });
            optionLeft.dataset.source[0].push(item.name);
            optionLeft.dataset.source[1].push(item.nodes.length);

            optionRight.legend.data[index] = item.name;
            optionRight.series[0].data[index] = {
                name: item.name,
                value: item.nodes.length
            }
        });
        this.chartLeft.setOption(optionLeft);
        this.chartRight.setOption(optionRight);
    },
    methods: {
        getClusterList: async function () {
            const result = await this.$http.get('clusters', {
                params: {
                    query: '',
                    pagenum: 1,
                    pagesize: 9999
                }
            });
            const res = result.data;
            if (res.meta.status !== 200) return this.$message.error(`获取集群列表失败：${res.meta.msg}`);

            this.clusterList = [];
            this.clusterListAll = res.data.clusters;
            this.clusterListAll.forEach(element => {
                if (element.nodes && element.nodes.length > 0) this.clusterList.push(element);
            });
        },
        getRowKeys: function (row) {
            return row.id;
        },
        clusterBrowse: async function (cid) {
            this.startProcess('集群运行状态信息');
            const result = await this.$http.get(`ha/status/${cid}`);
            const res = result.data;
            if (res.meta.status !== 201) return this.$message.error(`获取集群运行状态失败：${res.meta.msg}`);
            this.jobData.jobid = res.data.job_id;
            this.getProcessResult(500);
        },
        clusterStart: async function (cid) {
            this.startProcess('启动集群监听服务');
            const result = await this.$http.put(`clusters/startup/${cid}`);
            const res = result.data;
            if (res.meta.status !== 200) return this.$message.error(`启动集群监听服务失败：${res.meta.msg}`);
            this.jobData.jobid = res.data.job_id;
            this.getProcessResult(500);
        },
        clusterStop: async function (cid) {
            this.startProcess('停止集群监听服务');
            const result = await this.$http.put(`clusters/shutdown/${cid}`);
            const res = result.data;
            if (res.meta.status !== 200) return this.$message.error(`停止集群监听服务失败：${res.meta.msg}`);
            this.jobData.jobid = res.data.job_id;
            this.getProcessResult(500);
        },
        haBrowse: async function (cid) {
            this.startProcess('集群服务状态信息');
            const result = await this.$http.get(`ha/service/${cid}`);
            const res = result.data;
            if (res.meta.status !== 201) return this.$message.error(`获取集群服务状态失败：${res.meta.msg}`);
            this.jobData.jobid = res.data.job_id;
            this.getProcessResult(500);
        },
        haResume: async function (cid) {
            this.startProcess('恢复HA高可用功能');
            const result = await this.$http.put(`ha/resume/${cid}`);
            const res = result.data;
            if (res.meta.status !== 200) return this.$message.error(`恢复HA高可用功能失败：${res.meta.msg}`);
            this.jobData.jobid = res.data.job_id;
            this.getProcessResult(500);
        },
        haSuspend: async function (cid) {
            this.startProcess('暂停HA高可用功能');
            const result = await this.$http.put(`ha/suspend/${cid}`);
            const res = result.data;
            if (res.meta.status !== 200) return this.$message.error(`暂停HA高可用功能失败：${res.meta.msg}`);
            this.jobData.jobid = res.data.job_id;
            this.getProcessResult(500);
        },
        switchMaster: async function (cid, id) {
            this.startProcess('集群主备节点切换');
            const result = await this.$http.put(`ha/switch/${cid}/${id}`);
            const res = result.data;
            if (res.meta.status !== 200) return this.$message.error(`集群主备节点切换失败：${res.meta.msg}`);
            this.jobData.jobid = res.data.job_id;
            this.getProcessResult(500);
        },
        startProcess: function (dialogTitle) {
            this.dialogTitle = dialogTitle;
            this.processDialogVisible = true;
            this.inJobProcessing = true;
            this.refreshDataFlag = true;
            this.jobData = {
                jobid: '0',
                status: 0,
                steps: [
                    {
                        name: '正在准备操作步骤...',
                        message: '即将开始执行命令，请稍候...',
                        status: 0
                    }
                ]
            };
        },
        getProcessResult: function (timeout) {
            setTimeout(async () => {
                // 4c1d4dd8-c593-4975-8f03-1fe9c27563c5
                const result = await this.$http.get(`process/${this.jobData.jobid}`);
                const res = result.data;
                if (res.meta.status !== 201) return this.$message.error(`获取任务处理信息失败：${res.meta.msg}`);
                this.jobData = res.data;
                // console.log("jobid: " + this.jobData.jobid);

                // 任务如果非错误和完成状态，则循环监听任务回执消息
                const statusCode = res.data.status;
                if (statusCode !== -1 && statusCode !== 2) this.getProcessResult(2000);
                else this.inJobProcessing = false;
            }, timeout);
        },
        closeProcessDialog: function () {
            this.processDialogVisible = false;
            if (this.refreshDataFlag) this.getClusterList();
        }
    }
}
</script>

<style lang="less" scoped>
    .el-tag {
        margin: 7px
    }

    .hdgap .el-button {
        margin: 2px 0 2px 0;
    }

    .bdtop {
        border-top: 1px solid #eee;
    }

    .bdbottom {
        border-bottom: 1px solid #eee;
    }

    .v-center {
        display: flex;
        align-items: center;
    }

    .nodebox {
        position: relative;
        width: calc(100% - 10px);
        height: 30px;
        border-radius: 10px;
        margin: 5px;
        overflow: hidden;
    }

    .nodebox:before {
        content: '';
        position: absolute;
        left: 80px;
        top: 0px;
        height: 100%;
        border-right: 15px solid rgba(0, 0, 0, 0);
    }

    .nodebox .nodebox-prefix {
        display: inline-block;
        width: 80px;
        height: 100%;
        text-align: center;
        line-height: 28px;
        color: white;
        font-weight: 400;
    }

    .nodebox .nodebox-content {
        display: inline-block;
        width: calc(100% - 80px);
        height: 100%;
        text-align: center;
        line-height: 28px;
        font-weight: 900;
    }

    .nodebox-master {
        border: 1px solid #409EFF;
    }

    .nodebox-master .nodebox-prefix {
        background-color: #409EFF;
    }

    .nodebox-master .nodebox-content {
        color: #409EFF;
    }

    .nodebox-master:before {
        border-top: 28px solid #409EFF;
    }

    .nodebox-standby {
        border: 1px dashed #C4E1FF;
    }

    .nodebox-standby .nodebox-prefix {
        background-color: #C4E1FF;
    }

    .nodebox-standby .nodebox-content {
        color: #C4E1FF;
    }

    .nodebox-standby:before {
        border-top: 28px solid #C4E1FF;
    }

</style>